﻿using Levfier.Proccessors.Clippers;
using Levfier.Proccessors.Filters;
using Levfier.Proccessors.Gain;
using Levfier.Proccessors.Parameters;
using System;

namespace Levfier.Proccessors.Tubes
{
    /// <summary>
    /// ハードクリップ
    /// </summary>
    [Serializable]
    public class OriginalTube : ProccessorSetBase
    {
        #region 定数

        /// <summary>
        /// 粗さ/細かの基準周波数
        /// </summary>
        private static readonly double CoarseFineBaseFreq = 125.0d;

        #endregion

        /// <summary>
        /// 偶数/奇数 歪
        /// </summary>
        private ProccessParameter oddEvenParam;

        /// <summary>
        /// 粗い/細かい 歪
        /// </summary>
        private ProccessParameter coarseFineParam;

        /// <summary>
        /// 粗い/細かい の強さ
        /// </summary>
        private ProccessParameter coarseFineStrengthParam;

        /// <summary>
        /// 歪前のアシメゲイン
        /// </summary>
        private AsymmetricalLinearGain asymGainPre;

        /// <summary>
        /// 歪前のアシメゲイン
        /// </summary>
        private AsymmetricalLinearGain asymGainPost;

        /// <summary>
        /// 歪前のピーク
        /// 歪の細かさに関係する
        /// </summary>
        private HighShelfIIR highShelfIirPre;

        /// <summary>
        /// 歪後のピーク
        /// 歪の細かさに関係する
        /// </summary>
        private HighShelfIIR highShelfIirPost;

        /// <summary>
        /// ミドルクリッパー
        /// </summary>
        private MiddleClipper midClipper;

        /// <summary>
        /// 処理を行う
        /// </summary>
        /// <param name="input">入力</param>
        public override void Proccess(double[] input)
        {
            if (null == this.lowerChain)
            {
                return;
            }

            if (this.bypass)
            {
                this.ProccessBypass(input, this.lowerChain.GetBuffer());
                this.lowerChain.FlowSounds();
            }
            else
            {
                //最後尾のhighにバッファチェインを設定
                this.highShelfIirPost.SetLowerChain(this.lowerChain);

                // 最初のフィルタに音を流す
                this.highShelfIirPre.Proccess(input);
            }

            //↓自動的に呼び出される
            //後段のストリームへ流す
            //lowerChain.FlowSounds();
        }

        /// <summary>
        /// 何もしない
        /// </summary>
        /// <param name="input">入力</param>
        /// <param name="output">出力</param>
        protected override void InnerProccess(double[] inputBuffer, double[] outputBuffer)
        {
            //何もしない
        }

        /// <summary>
        /// パラメータを初期化する
        /// </summary>
        protected override void InitParameters()
        {
            //プロセッサを初期化する
            this.InitProccessors();

            //ミドルクリッパーのパラメータを全部登録
            foreach (ProccessParameter param in this.midClipper.GetParameters().Values)
            {
                if (!this.parameters.ContainsKey(param.Name))
                {
                    this.parameters.Add(param.Name, param);
                }
            }

            //奇数偶数
            this.oddEvenParam = new ProccessParameter();
            this.oddEvenParam.Name = "Dist Odd/Even";
            this.oddEvenParam.Value = 0.0d;
            this.oddEvenParam.MaxValue = 10.0d;
            this.oddEvenParam.MinValue = 0.0d;
            this.oddEvenParam.Step = 0.1d;
            this.oddEvenParam.ValueChanged += OddEven_ValueChanged;
            this.parameters.Add(this.oddEvenParam.Name, this.oddEvenParam);

            this.coarseFineParam = new ProccessParameter();
            this.coarseFineParam.Name = "Dist Coarse/Fine";
            this.coarseFineParam.Value = 7.0d;
            this.coarseFineParam.MaxValue = 20.0d;
            this.coarseFineParam.MinValue = 0.0d;
            this.coarseFineParam.Step = 0.1d;
            this.coarseFineParam.ValueChanged += CoarseFine_ValueChanged;
            this.parameters.Add(this.coarseFineParam.Name, this.coarseFineParam);

            this.coarseFineStrengthParam = new ProccessParameter();
            this.coarseFineStrengthParam.Name = "Dist Strength";
            this.coarseFineStrengthParam.Value = 0.0d;
            this.coarseFineStrengthParam.MaxValue = 10.0d;
            this.coarseFineStrengthParam.MinValue = -10.0d;
            this.coarseFineStrengthParam.Step = 0.1d;
            this.coarseFineStrengthParam.ValueChanged += CoarseFineStrength_ValueChanged;
            this.parameters.Add(this.coarseFineStrengthParam.Name, this.coarseFineStrengthParam);
        }

        /// <summary>
        /// 歪の特徴の強さの設定が変化した際の動作
        /// </summary>
        /// <param name="sender"></param>
        private void CoarseFineStrength_ValueChanged(object sender)
        {
            double gain = this.coarseFineStrengthParam.Value;
            this.highShelfIirPre.GetParameters()["Gain[dB]"].Value = gain;
            this.highShelfIirPost.GetParameters()["Gain[dB]"].Value = -gain;
        }

        /// <summary>
        /// 粗い・細かい の設定が変化した際の動作
        /// </summary>
        /// <param name="sender"></param>
        private void CoarseFine_ValueChanged(object sender)
        {
            double freq = CoarseFineBaseFreq * (1.0d + this.coarseFineParam.Value);
            this.highShelfIirPre.GetParameters()["Freq[Hz]"].Value = freq;
            this.highShelfIirPost.GetParameters()["Freq[Hz]"].Value = freq;
        }

        /// <summary>
        /// 偶数/奇数倍音の設定が変化した際の動作
        /// </summary>
        /// <param name="sender"></param>
        private void OddEven_ValueChanged(object sender)
        {
            //歪の前
            this.asymGainPre.GetParameters()["Gain2[dB]"].Value = this.oddEvenParam.Value;

            //歪の後
            this.asymGainPost.GetParameters()["Gain2[dB]"].Value = -this.oddEvenParam.Value;
        }

        private void InitProccessors()
        {
            //プロセッサを生成
            this.highShelfIirPre = new HighShelfIIR();
            this.asymGainPre = new AsymmetricalLinearGain();
            this.midClipper = new MiddleClipper();
            this.asymGainPost = new AsymmetricalLinearGain();
            this.highShelfIirPost = new HighShelfIIR();

            //プロセッサ同士を接続
            this.ChainProccessors(this.highShelfIirPre, this.asymGainPre);
            this.ChainProccessors(this.asymGainPre, this.midClipper);
            this.ChainProccessors(this.midClipper, this.asymGainPost);
            this.ChainProccessors(this.asymGainPost, this.highShelfIirPost);

        }
    }
}
